package com.yanqu.road.server.gameplayer.module.cmdstat;

import com.yanqu.road.entity.cmd.CmdStatisticsData;
import com.yanqu.road.logic.config.ChannelConfig;
import com.yanqu.road.server.protocol.GameProtocol;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.date.DateHelper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class UserCommandStatistics {

    private static Logger commandLogger = LogManager.getLogger("GAME_COMMAND");
    private static Logger secondLogger = LogManager.getLogger("SECOND_COMMAND");

    private long userId;

    public UserCommandStatistics(long userId) {
        this.userId = userId;
    }

    Map<Integer, CmdStatisticsData> commandStatisticsDataMap = new ConcurrentHashMap<>();

    /**
     * 每秒,每个协议的执行次数
     */
    Map<Long, Map<Integer, Integer>> commandSecondTimesMap = new ConcurrentHashMap<>();
    boolean waitKick = false;

    public void addCommandData(int cmdCode, long executeTime) {
        if (cmdCode == Protocol.S_PLAYER_PING) {
            return;
        }
        CmdStatisticsData data = commandStatisticsDataMap.get(cmdCode);
        if (data == null) {
            data = commandStatisticsDataMap.get(cmdCode);
            if (data == null) {
                data = new CmdStatisticsData(cmdCode);
                commandStatisticsDataMap.put(cmdCode, data);
            }
        }
        data.addTotalTimes(1);
        data.addTotalExecuteTime(executeTime/1000);


    }

    public void resetAndLog() {
        Map<Integer, CmdStatisticsData> temp = commandStatisticsDataMap;
        commandStatisticsDataMap = new ConcurrentHashMap<>();

        ArrayList<CmdStatisticsData> list = new ArrayList<>(temp.values());
        list.sort(Comparator.comparingLong(CmdStatisticsData::getTotalTimes));
        Collections.reverse(list);
        int num = 0;
        int totalTimes = 0;
        int costTime = 0;
        for (CmdStatisticsData data :list) {
            totalTimes += data.getTotalTimes();
            costTime += data.getTotalExecuteTime();
            if (data.getTotalTimes() <= 10) {
                continue;
            }
            num++;
            if (num <= 10) {
                commandLogger.info("player:{}, code:{} exeTimes: {}, exeCostTime: {},avgTime: {}", userId, data.getCode(), data.getTotalTimes(), data.getTotalExecuteTime(), data.getAvgTime());
            }

            data.setTotalExecuteTime(0);
            data.setTotalTimes(0);
        }
        if (totalTimes > 100) {
            commandLogger.info("player:{}, exeTotalTimes: {}, exeCostTime: {}", userId, totalTimes, costTime / 1000);
        }
    }

    /**
     * 监控到一秒内某个协议超过800次返回true,然后踢下线发邮件警告
     *
     * @param cmdCode
     * @return
     */
    public boolean monitoringCmdSecondTimes(int cmdCode) {
        if (cmdCode == Protocol.S_PLAYER_PING) {
            return false;
        }
        long currentSecond = DateHelper.getCurrentTime() / 1000;

        Map<Integer, Integer> map = commandSecondTimesMap.get(currentSecond);
        if (map == null) {
            map = new ConcurrentHashMap<>();
            commandSecondTimesMap.put(currentSecond, map);
        }
        Integer times = map.getOrDefault(cmdCode, 0);
        times++;
        map.put(cmdCode, times);

        if (times > ChannelConfig.SECOND_CMD_CHECK_TIMES) {
            if (!waitKick) {
                secondLogger.info("player:{}, cmd:{}, times:{}", userId, cmdCode, times);
                waitKick = true;
                return true;
            }
        }

        List<Long> needRemove = new ArrayList<>();
        for (Long second : commandSecondTimesMap.keySet()) {
            if (second < currentSecond) {
                needRemove.add(second);
            }
        }

        for (Long second : needRemove) {
            commandSecondTimesMap.remove(second);
        }

        return false;
    }

    public boolean isWaitKick(int code) {
        if (code == GameProtocol.S_KICK_OUT_PLAYER) {
            return false;
        }else if (code == GameProtocol.S_PLAYER_LOGOUT) {
            return false;
        }

        return waitKick;
    }
}
